package main

import (
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"fmt"
	pb "github.com/hyperledger/fabric/protos/peer"
	"errors"
	"encoding/json"
	"strings"
	"bytes"
)

type TokenListChaincode struct {

}
type Tokens struct {
	Name   string 	`json:"name"`					// 合约名称
	Symbol string   `json:"symbol"`					// 符号，简称
	Supply string 	`json:"supply"`					// 发行总量
	Owner  string 	`json:"owner"`					// 所有人
	Status int 		`json:"status"`
}

func main() {
	err := shim.Start(new(TokenListChaincode))
	if err != nil {
		fmt.Errorf(err.Error())
	}
}

func (t TokenListChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	return shim.Success(nil)
}

func (t TokenListChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {

	fnc, args := stub.GetFunctionAndParameters()
	if fnc == "" {
		return shim.Error("function is nil")
	} else if fnc == "fetch" {
		return t.Fetch(stub, args)
	} else if fnc == "query" {
		return t.Query(stub, args)
	} else if fnc == "add" {
		return t.Add(stub, args)
	} else if fnc == "queryString" {
		return t.QueryString(stub, args)
	} else if fnc == "isExist" {
		return t.IsExist(stub, args)
	}

	return shim.Success(nil)
}

func (t TokenListChaincode) Add(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	if len(args) != 4 {
		return shim.Error(parameterErr.Error())
	}
	tokenName, symbol, suply, owner := args[0], strings.ToLower(args[1]), args[2] ,has0xSuf(args[3])

	// 校验地址合法性
	if !checkAddress(owner) {
		return shim.Error(addressError.Error())
	}

	tb, _ := stub.GetState(owner)
	if tb != nil {
		return shim.Error("The address has created the token")
	}

	token := &Tokens{
		Name:   tokenName,
		Symbol: symbol,
		Supply: suply,
		Owner:  owner,
		Status: 0,
	}

	err := setToken(stub, token)
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success(nil)
}

// todo 修改完发币流程后，该方法作废
func (t TokenListChaincode) Fetch(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	if len(args) != 1 {
		return shim.Error(parameterErr.Error())
	}
	key := has0xSuf(args[0])

	// 校验地址合法性
	if !checkAddress(key) {
		return shim.Error(addressError.Error())
	}

	// 读取数据
	token, err := getToken(stub, key)
	if err != nil {
		return shim.Error(err.Error())
	}
	if token.Owner != key {
		return shim.Error(permissionErr.Error())
	}
	// 修改数据
	token.Status = 1
	tb, err := json.Marshal(token)
	if err != nil {
		return shim.Error(err.Error())
	}
	if err = stub.PutState(token.Owner, tb); err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success(tb)
}

func (t TokenListChaincode) Query(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	if len(args) != 1 {
		return shim.Error(parameterErr.Error())
	}
	addr := has0xSuf(args[0])
	// 校验地址合法性
	if !checkAddress(addr) {
		return shim.Error(addressError.Error())
	}
	tb, err := stub.GetState(addr)
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success(tb)
}

func (t TokenListChaincode) IsExist(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 1 {
		return shim.Error(parameterErr.Error())
	}
	symbol := strings.ToLower(args[0])
	selectorStr := `
		{
		   "selector": {
			  "symbol": "%s"
		   }
		}
	`
	queryStr := fmt.Sprintf(selectorStr, symbol)
	qrs, err := stub.GetQueryResult(queryStr)
	if err != nil {
		return shim.Error(err.Error())
	}
	defer qrs.Close()
	if qrs.HasNext() {
		qrsb, _ := qrs.Next()
		value := qrsb.Value
		// 如果代币存在，则将信息存放于错误内容中，供其他流程使用
		return shim.Error(string(value))
	}

	return shim.Success(nil)
}

func (t TokenListChaincode) QueryString(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 1 {
		return shim.Error(parameterErr.Error())
	}
	queryString := args[0]
	if queryString == "" {
		return shim.Error("queryString is nil")
	}
	qrs, err := stub.GetQueryResult(queryString)
	if err != nil {
		return shim.Error(err.Error())
	}
	defer qrs.Close()
	value, err := getListResult(qrs)
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(value)
}

func getToken(stub shim.ChaincodeStubInterface, key string) (*Tokens, error) {
	tb, err := stub.GetState(key)
	if err != nil {
		return nil, err
	}
	if tb == nil {
		return nil, errors.New("token is nil")
	}
	var token Tokens
	err = json.Unmarshal(tb, &token)
	return &token, err
}

func setToken(stub shim.ChaincodeStubInterface, tokens *Tokens) error {
	tb, err := json.Marshal(tokens)
	if err != nil {
		return err
	}
	return stub.PutState(tokens.Owner, tb)
}

var parameterErr = errors.New("parameter err")
var permissionErr = errors.New("permission denied")

func has0xSuf(addr string) string {
	if strings.Index(addr, "0x") != 0 {
		return strings.ToLower(addr)
	}
	return strings.ToLower(addr[2:])
}

var addressError = errors.New("address error")

func checkAddress(addrs ...string) bool {
	for i := range addrs {
		if len(has0xSuf(addrs[i])) != 40 {
			return false
		}
	}
	return true
}

func getListResult(resultsIterator shim.StateQueryIteratorInterface) ([]byte, error) {

	defer resultsIterator.Close()
	// buffer is a JSON array containing QueryRecords
	var buffer bytes.Buffer
	buffer.WriteString("[")

	bArrayMemberAlreadyWritten := false
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return nil, err
		}
		// Add a comma before array members, suppress it for the first array member
		if bArrayMemberAlreadyWritten == true {
			buffer.WriteString(",")
		}
		buffer.WriteString("{\"Key\":")
		buffer.WriteString("\"")
		buffer.WriteString(queryResponse.Key)
		buffer.WriteString("\"")

		buffer.WriteString(", \"Record\":")
		// Record is a JSON object, so we write as-is
		buffer.WriteString(string(queryResponse.Value))
		buffer.WriteString("}")
		bArrayMemberAlreadyWritten = true
	}
	buffer.WriteString("]")
	fmt.Printf("queryResult:\n%s\n", buffer.String())
	return buffer.Bytes(), nil
}